package com.socket_2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;

/**
 * @author: bird
 * @description: 使用netty 构建一个服务端
 * netty-服务端创建的步骤：
 * 第一步：创建两个线程池 分别是boss和worker.
 * 第二步：加载ServerBootstrap的启动类的配置信息
 * 第三步：绑定端口--入口
 * 第四步：启动netty--服务端
 */
public class Server {


    // netty 服务端的启动流程
    public static void main(String[] args) throws Exception {

/**<>首先需要先创建两个线程池  一个是boss线程池 一个是worker线程池</>
 * boss线程池: 专门接收socket连接 然后创建channel对象通道。并且把创建的channel交给serverBootstrap
 * 初始化指定的serverSocketChannelFactory来处理请求，然后boss继续处理socket请求.
 *
 * worker线程池:专门处理serverSocketChannelFactory请求的数据.
 * serverSocketChannelFactory会从worker线程池中拿出一个worker线程继续处理请求。一个worker线程可以处理多个
 */
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //option主要是针对boss线程组，child主要是针对worker线程组
        try {

            /** <构建ServerBootStrap对象>
             * 作用一：用来负责初始化netty的服务器.
             * 作用二：并且开始监听端口的socket请求.
             */
            ServerBootstrap b = new ServerBootstrap();
            // 添加boss和worker线程池
            b.group(bossGroup, workerGroup)
                    // 指定以什么样式的通道来处理请求数据 分为 nio和oio两个实现类
                    .channel(NioServerSocketChannel.class)
                    //childOption:  设置为true  开启angle算法，要求每条客户端有数据就会立即发送
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 设置服务端处理客户端连接请求的队列大小 如果有多个客户端连接 服务端不能及时处理连接 那么将会连接放到队列中
                    .childOption(ChannelOption.SO_BACKLOG, 100)
                    //设置端口号是否可以重复的被利用
                    .childOption(ChannelOption.SO_REUSEADDR, false)
                   // .option(ChannelOption.SO_REUSEADDR,Boolean.FALSE)
                    // 设置keepalive=true,如果两小时内没有数据交流 那么就自动的发送一个消息
                   .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //设置接收缓冲区和发送缓冲区的大小，接收缓冲区用于保存网络协议站内收到的数据，直到应用程序读取成功，发送缓冲区用于保存发送数据，直到发送成功
                   .childOption(ChannelOption.SO_SNDBUF, 2048)
                    // .childOption(ChannelOption.SO_LINGER,1024)
                    // childAttr：在每次创创建客户端连接的时候 给每个客户端设置一些基本的属性
                   .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    // handler 在服务端启动的过程需要加载的一些处理的逻辑 加载到channel
                    .handler(new ServerHandler())
                    // 这个主要是对应的workerGroup 用来处理每条新连接的数据流的逻辑处理
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {

                            //pipeline: 对应的是每条新连接的数据流的读写操作 逻辑的处理
                            // ch.pipeline().addLast(new AuthHandler());
                            // ch.pipeline().addLast(new AuthHandler());
                            // ch.pipeline().addLast(new AuthHandler());
                            //..

                        }
                    });
            // 绑定端口号 当方法执行到这里的时候 就可以接受指定端口上的socket连接。一个serverBootstrap 可以绑定多个端口号。
            // 一个端口号对应一个boss线程
            ChannelFuture f = b.bind(8888).sync();
            // 关闭channel 同步
            f.channel().closeFuture().sync();
        } finally {
            System.out.println("++++++++++++退出++++++++++++++++++++");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main1(String[] args) {
        int num = 1 << 4;
        // a-10 b-11 c-12 d-13 e-14 f-15
        int port = 0xFFFF;// F F F F 15 15 15 15
        // 15*0 +15*1 +15*2 +15*3r

        
        System.out.println("1<<4位运算结果：" + num);
        System.out.println("port: " + port);
    }
}


/*boss线程和worker线程
 *  serverBootstrap监听的一个端口对应一个boss线程，它们是一一对应的。
 *
 *
 *
 *
 * */

